<html><head><meta charset="utf-8"><title>22 使用映射类型得到新的类型-慕课专栏</title>
			<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
			<meta name="renderer" content="webkit">
			<meta property="qc:admins" content="77103107776157736375">
			<meta property="wb:webmaster" content="c4f857219bfae3cb">
			<meta http-equiv="Access-Control-Allow-Origin" content="*">
			<meta http-equiv="Cache-Control" content="no-transform ">
			<meta http-equiv="Cache-Control" content="no-siteapp">
			<link rel="apple-touch-icon" sizes="76x76" href="https://www.imooc.com/static/img/common/touch-icon-ipad.png">
			<link rel="apple-touch-icon" sizes="120x120" href="https://www.imooc.com/static/img/common/touch-icon-iphone-retina.png">
			<link rel="apple-touch-icon" sizes="152x152" href="https://www.imooc.com/static/img/common/touch-icon-ipad-retina.png">
			<link href="https://moco.imooc.com/captcha/style/captcha.min.css" rel="stylesheet">
			<link rel="stylesheet" href="https://www.imooc.com/static/moco/v1.0/dist/css/moco.min.css?t=201907021539" type="text/css">
			<link rel="stylesheet" href="https://www.imooc.com/static/lib/swiper/swiper-3.4.2.min.css?t=201907021539">
			<link rel="stylesheet" href="https://static.mukewang.com/static/css/??base.css,common/common-less.css?t=2.5,column/zhuanlanChapter-less.css?t=2.5,course/inc/course_tipoff-less.css?t=2.5?v=201907051055" type="text/css">
			<link charset="utf-8" rel="stylesheet" href="https://www.imooc.com/static/lib/ueditor/themes/imooc/css/ueditor.css?v=201907021539"><link rel="stylesheet" href="https://www.imooc.com/static/lib/baiduShare/api/css/share_style0_16.css?v=6aba13f0.css"></head>
			<body><div id="main">

<div class="container clearfix" id="top" style="display: block; width: 1134px;">
    
    <div class="center_con js-center_con l" style="width: 1134px;">
        <div class="article-con">
                            <!-- 买过的阅读 -->
                <div class="map">
                    <a href="/read" target="_blank"><i class="imv2-feather-o"></i></a>
                    <a href="/read/35" target="_blank">零基础学透 TypeScript</a>
                    <a href="" target="_blank">
                        <span>
                            / 3-9 22 使用映射类型得到新的类型
                        </span>
                    </a>
                </div>

            


            <div class="art-title" style="margin-top: 0px;">
                22 使用映射类型得到新的类型
            </div>
            <div class="art-info">
                
                <span>
                    更新时间：2019-07-01 11:43:19
                </span>
            </div>
            <div class="art-top">
                                <img src="https://img2.mukewang.com/5d0c3bbd00019ead06400359.jpg" alt="">
                                                <div class="famous-word-box">
                    <img src="https://www.imooc.com/static/img/column/bg-l.png" alt="" class="bg1 bg">
                    <img src="https://www.imooc.com/static/img/column/bg-r.png" alt="" class="bg2 bg">
                    <div class="famous-word">知识是一种快乐，而好奇则是知识的萌芽。<p class="author">——培根</p></div>
                </div>
                            </div>
            <div class="art-content js-lookimg">
                <div><div class="cl-preview-section"><h3 id="基础">3.9.1 基础</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">TS 提供了借助旧类型创建一个新类型的方式，也就是映射类型，它可以用相同的形式去转换旧类型中每个属性。来看个例子：</p>
</div><div class="cl-preview-section"><pre class="  language-typescript"><code class="prism  language-typescript"><span class="token keyword">interface</span> <span class="token class-name">Info</span> <span class="token punctuation">{</span>
  age<span class="token punctuation">:</span> <span class="token keyword">number</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">我们可以使用这个接口实现一个有且仅有一个 age 属性的对象，但如果我们想再创建一个只读版本的同款对象，那我们可能需要再重新定义一个接口，然后让 age 属性 readonly。如果接口就这么简单，你确实可以这么做，但是如果属性多了，而且这个结构以后会变，那就比较麻烦了。这种情况我们可以使用映射类型，下面来看例子：</p>
</div><div class="cl-preview-section"><pre class="  language-typescript"><code class="prism  language-typescript"><span class="token keyword">interface</span> <span class="token class-name">Info</span> <span class="token punctuation">{</span>
  age<span class="token punctuation">:</span> <span class="token keyword">number</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">type</span> ReadonlyType<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token punctuation">{</span> readonly <span class="token punctuation">[</span>P <span class="token keyword">in</span> keyof T<span class="token punctuation">]</span><span class="token punctuation">:</span> T<span class="token punctuation">[</span>P<span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token comment">// 这里定义了一个ReadonlyType&lt;T&gt;映射类型</span>
<span class="token keyword">type</span> ReadonlyInfo <span class="token operator">=</span> ReadonlyType<span class="token operator">&lt;</span>Info<span class="token operator">&gt;</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> info<span class="token punctuation">:</span> ReadonlyInfo <span class="token operator">=</span> <span class="token punctuation">{</span>
  age<span class="token punctuation">:</span> <span class="token number">18</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
info<span class="token punctuation">.</span>age <span class="token operator">=</span> <span class="token number">28</span><span class="token punctuation">;</span> <span class="token comment">// error Cannot assign to 'age' because it is a constant or a read-only property</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">这个例子展示了如何通过一个普通的接口创建一个每个属性都只读的接口，这个过程有点像定义了一个函数，这个函数会遍历传入对象的每个属性并做处理。同理你也可以创建一个每个属性都是可选属性的接口：</p>
</div><div class="cl-preview-section"><pre class="  language-typescript"><code class="prism  language-typescript"><span class="token keyword">interface</span> <span class="token class-name">Info</span> <span class="token punctuation">{</span>
  age<span class="token punctuation">:</span> <span class="token keyword">number</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">type</span> ReadonlyType<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token punctuation">{</span> readonly <span class="token punctuation">[</span>P <span class="token keyword">in</span> keyof T<span class="token punctuation">]</span><span class="token operator">?</span><span class="token punctuation">:</span> T<span class="token punctuation">[</span>P<span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">type</span> ReadonlyInfo <span class="token operator">=</span> ReadonlyType<span class="token operator">&lt;</span>Info<span class="token operator">&gt;</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> info<span class="token punctuation">:</span> ReadonlyInfo <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">注意了，我们在这里用到了一个新的操作符 in，TS 内部使用了 for … in，定义映射类型，这里涉及到三个部分：</p>
</div><div class="cl-preview-section"><ul>
<li style="font-size: 20px; line-height: 38px;">类型变量，也就是上例中的 P，它就像 for…in 循环中定义的变量，用来在每次遍历中绑定当前遍历到的属性名；</li>
<li style="font-size: 20px; line-height: 38px;">属性名联合，也就是上例中<code>keyof T</code>，它返回对象 T 的属性名联合；</li>
<li style="font-size: 20px; line-height: 38px;">属性的结果类型，也就是 T[P]。</li>
</ul>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">因为这两个需求较为常用，所以 TS 内置了这两种映射类型，无需定义即可使用，它们分别是<code>Readonly</code>和<code>Partial</code>。还有两个内置的映射类型分别是<code>Pick</code>和<code>Record</code>，它们的实现如下：</p>
</div><div class="cl-preview-section"><pre class="  language-typescript"><code class="prism  language-typescript"><span class="token keyword">type</span> Pick<span class="token operator">&lt;</span>T<span class="token punctuation">,</span> K <span class="token keyword">extends</span> <span class="token class-name">keyof</span> T<span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token punctuation">[</span>P <span class="token keyword">in</span> K<span class="token punctuation">]</span><span class="token punctuation">:</span> T<span class="token punctuation">[</span>P<span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">type</span> Record<span class="token operator">&lt;</span>K <span class="token keyword">extends</span> <span class="token class-name">keyof</span> <span class="token keyword">any</span><span class="token punctuation">,</span> T<span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token punctuation">[</span>P <span class="token keyword">in</span> K<span class="token punctuation">]</span><span class="token punctuation">:</span> T <span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">先来使用一下 Pick，官方文档的例子并不完整，我们来看完整的例子：</p>
</div><div class="cl-preview-section"><pre class="  language-typescript"><code class="prism  language-typescript"><span class="token keyword">interface</span> <span class="token class-name">Info</span> <span class="token punctuation">{</span>
  name<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
  age<span class="token punctuation">:</span> <span class="token keyword">number</span><span class="token punctuation">;</span>
  address<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">const</span> info<span class="token punctuation">:</span> Info <span class="token operator">=</span> <span class="token punctuation">{</span>
  name<span class="token punctuation">:</span> <span class="token string">"lison"</span><span class="token punctuation">,</span>
  age<span class="token punctuation">:</span> <span class="token number">18</span><span class="token punctuation">,</span>
  address<span class="token punctuation">:</span> <span class="token string">"beijing"</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">function</span> pick<span class="token operator">&lt;</span>T<span class="token punctuation">,</span> K <span class="token keyword">extends</span> <span class="token class-name">keyof</span> T<span class="token operator">&gt;</span><span class="token punctuation">(</span>obj<span class="token punctuation">:</span> T<span class="token punctuation">,</span> keys<span class="token punctuation">:</span> K<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">:</span> Pick<span class="token operator">&lt;</span>T<span class="token punctuation">,</span> K<span class="token operator">&gt;</span> <span class="token punctuation">{</span> <span class="token comment">// 这里我们定义一个pick函数，用来返回一个对象中指定字段的值组成的对象</span>
  <span class="token keyword">let</span> res <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token keyword">as</span> Pick<span class="token operator">&lt;</span>T<span class="token punctuation">,</span> K<span class="token operator">&gt;</span><span class="token punctuation">;</span>
  keys<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span>key <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    res<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> obj<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> res<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">const</span> nameAndAddress <span class="token operator">=</span> <span class="token function">pick</span><span class="token punctuation">(</span>info<span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">"name"</span><span class="token punctuation">,</span> <span class="token string">"address"</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// { name: 'lison', address: 'beijing' }</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">另外一个就是 Record，它适用于将一个对象中的每一个属性转换为其他值的场景，来看例子：</p>
</div><div class="cl-preview-section"><pre class="  language-typescript"><code class="prism  language-typescript"><span class="token keyword">function</span> mapObject<span class="token operator">&lt;</span>K <span class="token keyword">extends</span> <span class="token class-name">string</span> <span class="token operator">|</span> <span class="token keyword">number</span><span class="token punctuation">,</span> T<span class="token punctuation">,</span> U<span class="token operator">&gt;</span><span class="token punctuation">(</span>
  obj<span class="token punctuation">:</span> Record<span class="token operator">&lt;</span>K<span class="token punctuation">,</span> T<span class="token operator">&gt;</span><span class="token punctuation">,</span>
  f<span class="token punctuation">:</span> <span class="token punctuation">(</span>x<span class="token punctuation">:</span> T<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> U
<span class="token punctuation">)</span><span class="token punctuation">:</span> Record<span class="token operator">&lt;</span>K<span class="token punctuation">,</span> U<span class="token operator">&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">let</span> res <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token keyword">as</span> Record<span class="token operator">&lt;</span>K<span class="token punctuation">,</span> U<span class="token operator">&gt;</span><span class="token punctuation">;</span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> key <span class="token keyword">in</span> obj<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    res<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">f</span><span class="token punctuation">(</span>obj<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">return</span> res<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">const</span> names <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token number">0</span><span class="token punctuation">:</span> <span class="token string">"hello"</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">:</span> <span class="token string">"world"</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">:</span> <span class="token string">"bye"</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> lengths <span class="token operator">=</span> <span class="token function">mapObject</span><span class="token punctuation">(</span>names<span class="token punctuation">,</span> s <span class="token operator">=&gt;</span> s<span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// { 0: 5, 1: 5, 2: 3 }</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">我们输入的对象属性值为字符串类型，输出的对象属性值为数值类型。</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">讲完这四个内置的映射类型之后，我们需要讲一个概念——同态。同态在维基百科的解释是：两个相同类型的代数结构之间的结构保持映射。这四个内置映射类型中，Readonly、Partial 和 Pick 是同态的，而 Record 不是，因为 Record 映射出的对象属性值是新的，和输入的值的属性值不同。</p>
</div><div class="cl-preview-section"><h3 id="由映射类型进行推断">3.9.2 由映射类型进行推断</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">我们学习了使用映射类型包装一个类型的属性后，也可以进行逆向操作，也就是拆包，先来看我们的包装操作：</p>
</div><div class="cl-preview-section"><pre class="  language-typescript"><code class="prism  language-typescript"><span class="token keyword">type</span> Proxy<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment">// 这里定义一个映射类型，他将一个属性拆分成get/set方法</span>
  <span class="token keyword">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> T<span class="token punctuation">;</span>
  <span class="token keyword">set</span><span class="token punctuation">(</span>value<span class="token punctuation">:</span> T<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">type</span> Proxify<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token punctuation">[</span>P <span class="token keyword">in</span> keyof T<span class="token punctuation">]</span><span class="token punctuation">:</span> Proxy<span class="token operator">&lt;</span>T<span class="token punctuation">[</span>P<span class="token punctuation">]</span><span class="token operator">&gt;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token comment">// 这里再定义一个映射类型，将一个对象的所有属性值类型都变为Proxy&lt;T&gt;处理之后的类型</span>
<span class="token keyword">function</span> proxify<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span><span class="token punctuation">(</span>obj<span class="token punctuation">:</span> T<span class="token punctuation">)</span><span class="token punctuation">:</span> Proxify<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> <span class="token punctuation">{</span> <span class="token comment">// 这里定义一个proxify函数，用来将对象中所有属性的属性值改为一个包含get和set方法的对象</span>
  <span class="token keyword">let</span> result <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token keyword">as</span> Proxify<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span><span class="token punctuation">;</span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> key <span class="token keyword">in</span> obj<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    result<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
      <span class="token keyword">get</span><span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> obj<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">,</span>
      <span class="token keyword">set</span><span class="token punctuation">:</span> value <span class="token operator">=&gt;</span> <span class="token punctuation">(</span>obj<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> value<span class="token punctuation">)</span>
    <span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">return</span> result<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">let</span> props <span class="token operator">=</span> <span class="token punctuation">{</span>
  name<span class="token punctuation">:</span> <span class="token string">"lison"</span><span class="token punctuation">,</span>
  age<span class="token punctuation">:</span> <span class="token number">18</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> proxyProps <span class="token operator">=</span> <span class="token function">proxify</span><span class="token punctuation">(</span>props<span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>proxyProps<span class="token punctuation">.</span>name<span class="token punctuation">.</span><span class="token keyword">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// "lison"</span>
proxyProps<span class="token punctuation">.</span>name<span class="token punctuation">.</span><span class="token keyword">set</span><span class="token punctuation">(</span><span class="token string">"li"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">我们来看下这个例子，这个例子我们定义了一个函数，这个函数可以把传入的对象的每个属性的值替换为一个包含 get 和 set 两个方法的对象。最后我们获取某个值的时候，比如 name，就使用 proxyProps.name.get()方法获取它的值，使用 proxyProps.name.set()方法修改 name 的值。</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">接下来我们来看如何进行拆包：</p>
</div><div class="cl-preview-section"><pre class="  language-typescript"><code class="prism  language-typescript"><span class="token keyword">function</span> unproxify<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span><span class="token punctuation">(</span>t<span class="token punctuation">:</span> Proxify<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span><span class="token punctuation">)</span><span class="token punctuation">:</span> T <span class="token punctuation">{</span> <span class="token comment">// 这里我们定义一个拆包函数，其实就是利用每个属性的get方法获取到当前属性值，然后将原本是包含get和set方法的对象改为这个属性值</span>
  <span class="token keyword">let</span> result <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token keyword">as</span> T<span class="token punctuation">;</span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> k <span class="token keyword">in</span> t<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    result<span class="token punctuation">[</span>k<span class="token punctuation">]</span> <span class="token operator">=</span> t<span class="token punctuation">[</span>k<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token keyword">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 这里通过调用属性值这个对象的get方法获取到属性值，然后赋给这个属性，替换掉这个对象</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">return</span> result<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">let</span> originalProps <span class="token operator">=</span> <span class="token function">unproxify</span><span class="token punctuation">(</span>proxyProps<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><h3 id="增加或移除特定修饰符">3.9.3 增加或移除特定修饰符</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">TS 在 2.8 版本为映射类型增加了增加或移除特定修饰符的能力，使用<code>+</code>和<code>-</code>符号作为前缀来指定增加还是删除修饰符。首先来看我们如何通过映射类型为一个接口的每个属性增加修饰符，我们这里使用+前缀：</p>
</div><div class="cl-preview-section"><pre class="  language-typescript"><code class="prism  language-typescript"><span class="token keyword">interface</span> <span class="token class-name">Info</span> <span class="token punctuation">{</span>
  name<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
  age<span class="token punctuation">:</span> <span class="token keyword">number</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">type</span> ReadonlyInfo<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token operator">+</span>readonly <span class="token punctuation">[</span>P <span class="token keyword">in</span> keyof T<span class="token punctuation">]</span><span class="token operator">+</span><span class="token operator">?</span><span class="token punctuation">:</span> T<span class="token punctuation">[</span>P<span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> info<span class="token punctuation">:</span> ReadonlyInfo<span class="token operator">&lt;</span>Info<span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  name<span class="token punctuation">:</span> <span class="token string">"lison"</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
info<span class="token punctuation">.</span>name <span class="token operator">=</span> <span class="token string">""</span><span class="token punctuation">;</span> <span class="token comment">// error</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">这个例子中，经过 ReadonlyInfo 创建的接口类型，属性是可选的，所以我们在定义 info 的时候没有写 age 属性也没问题，同时每个属性是只读的，所以我们修改 name 的值的时候报错。我们通过+前缀增加了 readonly 和?修饰符。当然，增加的时候，这个+前缀可以省略，也就是说，上面的写法和<code>type ReadonlyInfo = { readonly [P in keyof T]?: T[P] }</code>是一样的。我们再来看下怎么删除修饰符：</p>
</div><div class="cl-preview-section"><pre class="  language-typescript"><code class="prism  language-typescript"><span class="token keyword">interface</span> <span class="token class-name">Info</span> <span class="token punctuation">{</span>
  name<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
  age<span class="token punctuation">:</span> <span class="token keyword">number</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">type</span> RemoveModifier<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token operator">-</span>readonly <span class="token punctuation">[</span>P <span class="token keyword">in</span> keyof T<span class="token punctuation">]</span><span class="token operator">-</span><span class="token operator">?</span><span class="token punctuation">:</span> T<span class="token punctuation">[</span>p<span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">type</span> InfoType <span class="token operator">=</span> RemoveModifier<span class="token operator">&lt;</span>Readonly<span class="token operator">&lt;</span>Partial<span class="token operator">&lt;</span>Info<span class="token operator">&gt;&gt;&gt;</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> info1<span class="token punctuation">:</span> InfoType <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token comment">// error missing "age"</span>
  name<span class="token punctuation">:</span> <span class="token string">"lison"</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> info2<span class="token punctuation">:</span> InfoType <span class="token operator">=</span> <span class="token punctuation">{</span>
  name<span class="token punctuation">:</span> <span class="token string">"lison"</span><span class="token punctuation">,</span>
  age<span class="token punctuation">:</span> <span class="token number">18</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
info2<span class="token punctuation">.</span>name <span class="token operator">=</span> <span class="token string">""</span><span class="token punctuation">;</span> <span class="token comment">// right, can edit</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">这个例子我们定义了去掉修饰符的映射类型 RemoveModifier，<code>Readonly&lt;Partial&lt;Info&gt;&gt;</code>则是返回一个既属性可选又只读的接口类型，所以 InfoType 类型则表示属性必含而且非只读。</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">TS 内置了一个映射类型<code>Required&lt;T&gt;</code>，使用它可以去掉 T 所有属性的<code>?</code>修饰符。</p>
</div><div class="cl-preview-section"><h3 id="keyof-和映射类型在-2.9-的升级">3.9.4 keyof 和映射类型在 2.9 的升级</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">TS 在 2.9 版本中，keyof 和映射类型支持用 number 和 symbol 命名的属性，我们先来看 keyof 的例子：</p>
</div><div class="cl-preview-section"><pre class="  language-typescript"><code class="prism  language-typescript"><span class="token keyword">const</span> stringIndex <span class="token operator">=</span> <span class="token string">"a"</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> numberIndex <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> symbolIndex <span class="token operator">=</span> <span class="token function">Symbol</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">type</span> Obj <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token punctuation">[</span>stringIndex<span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
  <span class="token punctuation">[</span>numberIndex<span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token keyword">number</span><span class="token punctuation">;</span>
  <span class="token punctuation">[</span>symbolIndex<span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token keyword">symbol</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">type</span> keys <span class="token operator">=</span> keyof Obj<span class="token punctuation">;</span>
<span class="token keyword">let</span> key<span class="token punctuation">:</span> keys <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span> <span class="token comment">// error</span>
<span class="token keyword">let</span> key<span class="token punctuation">:</span> keys <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token comment">// right</span>
<span class="token keyword">let</span> key<span class="token punctuation">:</span> keys <span class="token operator">=</span> <span class="token string">"b"</span><span class="token punctuation">;</span> <span class="token comment">// error</span>
<span class="token keyword">let</span> key<span class="token punctuation">:</span> keys <span class="token operator">=</span> <span class="token string">"a"</span><span class="token punctuation">;</span> <span class="token comment">// right</span>
<span class="token keyword">let</span> key<span class="token punctuation">:</span> keys <span class="token operator">=</span> <span class="token function">Symbol</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// error</span>
<span class="token keyword">let</span> key<span class="token punctuation">:</span> keys <span class="token operator">=</span> symbolIndex<span class="token punctuation">;</span> <span class="token comment">// right</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">再来看个映射类型的例子：</p>
</div><div class="cl-preview-section"><pre class="  language-typescript"><code class="prism  language-typescript"><span class="token keyword">const</span> stringIndex <span class="token operator">=</span> <span class="token string">"a"</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> numberIndex <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> symbolIndex <span class="token operator">=</span> <span class="token function">Symbol</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">type</span> Obj <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token punctuation">[</span>stringIndex<span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
  <span class="token punctuation">[</span>numberIndex<span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token keyword">number</span><span class="token punctuation">;</span>
  <span class="token punctuation">[</span>symbolIndex<span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token keyword">symbol</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">type</span> ReadonlyType<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token punctuation">{</span> readonly <span class="token punctuation">[</span>P <span class="token keyword">in</span> keyof T<span class="token punctuation">]</span><span class="token operator">?</span><span class="token punctuation">:</span> T<span class="token punctuation">[</span>P<span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> obj<span class="token punctuation">:</span> ReadonlyType<span class="token operator">&lt;</span>Obj<span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  a<span class="token punctuation">:</span> <span class="token string">"aa"</span><span class="token punctuation">,</span>
  <span class="token number">1</span><span class="token punctuation">:</span> <span class="token number">11</span><span class="token punctuation">,</span>
  <span class="token punctuation">[</span>symbolIndex<span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token function">Symbol</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
obj<span class="token punctuation">.</span>a <span class="token operator">=</span> <span class="token string">"bb"</span><span class="token punctuation">;</span> <span class="token comment">// error Cannot assign to 'a' because it is a read-only property</span>
obj<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">22</span><span class="token punctuation">;</span> <span class="token comment">// error Cannot assign to '1' because it is a read-only property</span>
obj<span class="token punctuation">[</span>symbolIndex<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">Symbol</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// error Cannot assign to '[symbolIndex]' because it is a read-only property</span>
</code></pre>
</div><div class="cl-preview-section"><h3 id="元组和数组上的映射类型">3.9.5 元组和数组上的映射类型</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">TS 在 3.1 版本中，在元组和数组上的映射类型会生成新的元组和数组，并不会创建一个新的类型，这个类型上会具有 push、pop 等数组方法和数组属性。来看例子：</p>
</div><div class="cl-preview-section"><pre class="  language-typescript"><code class="prism  language-typescript"><span class="token keyword">type</span> MapToPromise<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token punctuation">[</span>K <span class="token keyword">in</span> keyof T<span class="token punctuation">]</span><span class="token punctuation">:</span> Promise<span class="token operator">&lt;</span>T<span class="token punctuation">[</span>K<span class="token punctuation">]</span><span class="token operator">&gt;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">type</span> Tuple <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token keyword">number</span><span class="token punctuation">,</span> <span class="token keyword">string</span><span class="token punctuation">,</span> <span class="token keyword">boolean</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token keyword">type</span> promiseTuple <span class="token operator">=</span> MapToPromise<span class="token operator">&lt;</span>Tuple<span class="token operator">&gt;</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> tuple<span class="token punctuation">:</span> promiseTuple <span class="token operator">=</span> <span class="token punctuation">[</span>
  <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token punctuation">(</span>resolve<span class="token punctuation">,</span> reject<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">resolve</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token punctuation">(</span>resolve<span class="token punctuation">,</span> reject<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">resolve</span><span class="token punctuation">(</span><span class="token string">"a"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token punctuation">(</span>resolve<span class="token punctuation">,</span> reject<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">resolve</span><span class="token punctuation">(</span><span class="token keyword">false</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">]</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">这个例子中定义了一个MapToPromise映射类型。它返回一个将传入的类型的所有字段的值转为Promise，且Promise的resolve回调函数的参数类型为这个字段类型。我们定义了一个元组Tuple，元素类型分别为number、string和boolean，使用MapToPromise映射类型将这个元组类型传入，并且返回一个promiseTuple类型。当我们指定变量tuple的类型为promiseTuple后，它的三个元素类型都是一个Promise，且resolve的参数类型依次为number、string和boolean。</p>
</div><div class="cl-preview-section"><h3 id="本节小结">本节小结</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">本小节我们学习了映射类型的相关知识，我们学习了映射类型的基础应用，它的定义和使用像极了函数的定义和使用。函数是处理实际的值，而映射类型处理的是类型。我们还通过一个例子学习了由映射类型进行推断，根据映射类型推断出处理前的类型，也就是拆包操作。通过增加或移除特定修饰符"+“和”-“可以实现给字段添加或移除一些readonly等修饰符，但用的最多的是”-"。因为如果需要给某个字段加修饰符，"+"是可以省略不写的。最后我们补充了两个TypeScript在后面升级中对映射类型的更新。</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">下个小节我们将对前面讲TypeScript中补充的六个类型中跳过的unknown类型进行详细补充学习。<br>
<img src="http://img.mukewang.com/5d03461e0001dfa216000536.jpg" alt="图片描述" data-original="http://img.mukewang.com/5d03461e0001dfa216000536.jpg" class="" style="cursor: pointer;"></p>
</div></div>
            </div>
                            <!-- 买过的阅读 -->
                <div class="art-next-prev clearfix">
                                                                        <!-- 已买且开放 或者可以试读 -->
                            <a href="/read/35/article/358">
                                                    <div class="prev l clearfix">
                                <div class="icon l">
                                    <i class="imv2-arrow3_l"></i>
                                </div>
                                <p>
                                    21 索引类型：获取索引类型和索引值类型
                                </p>
                            </div>
                        </a>
                                                                                            <!-- 已买且开放 或者可以试读 -->
                            <a href="/read/35/article/360">
                                                    <div class="next r clearfix">
                                <p>
                                    23 前面跳过的unkown类型详解
                                </p>
                                <div class="icon r">
                                    <i class="imv2-arrow3_r"></i>
                                </div>

                            </div>
                        </a>
                                    </div>
                    </div>
        <div class="comments-con js-comments-con" id="coments_con">
        </div>



    </div>
    
    
    

</div>
 
<!-- 专栏介绍页专栏评价 -->

<!-- 专栏介绍页底部三条评价 -->

<!-- 专栏阅读页弹层目录和介绍页页面目录 -->

<!-- 专栏阅读页发布回复 -->

<!-- 专栏阅读页发布评论 -->

<!-- 专栏阅读页底部评论 -->

<!-- 专栏阅读 单个 评论 -->

<!-- 新增回复和展开三条以外回复 -->

<!-- 立即订阅的弹窗 -->












</div></body></html>